use crate::prelude::get_my_pool;

use super::parser::parse_exp;
use evalexpr::*;
use sqlx::{Column, Executor, Row, TypeInfo};
use num_traits::cast::ToPrimitive;
#[derive(Debug, Clone)]
pub struct ExprParser {
    columns: Vec<(String, String)>,
    precompiled: Node,
}
impl ExprParser {
    pub async fn from(exp: &str, table_name: &str) -> anyhow::Result<Self> {
        let (col_names, exp) = parse_exp(exp)?;
        let precompiled: Node = build_operator_tree(&exp)?;
        let sql = format!("select {} from {table_name}", col_names.join(","));
        let pool = get_my_pool();
        let d = pool.describe(&sql).await?;
        let mut columns: Vec<(String, String)> = Vec::new();
        for col in d.columns {
            columns.push((col.name().to_owned(), col.type_info().name().to_owned()));
        }
        Ok(Self {
            columns,
            precompiled,
        })
    }
    pub fn get_col_names(&self) -> Vec<(&str,&str)> {
        self.columns.iter().map(|c| (c.0.as_str(),c.1.as_str())).collect()
    }
    pub fn eval(&self,context:HashMapContext) -> anyhow::Result<Value> {
        Ok(self.precompiled.eval_with_context(&context)?)
    }
}
pub async fn get_expr_context_from_table(
    table_name: &str,
    cols: Vec<(&str,&str)>,
    where_cause: &str,
) -> anyhow::Result<HashMapContext> {
    let sql = format!(
        "select {} from {table_name} where {where_cause}",
        cols.iter()
            .map(|c| c.0)
            .collect::<Vec<&str>>()
            .join(",")
    );
    let row = sqlx::query(&sql).fetch_one(get_my_pool()).await?;
    let mut context: HashMapContext = HashMapContext::new();
    for (col, tp) in cols.iter() {
        let cname=col.to_string();
        match *tp {
            "BOOLEAN" => {
                let v: Option<bool> = row.try_get(col)?;
                if let Some(v) = v {
                    context.set_value(cname, Value::from(v))?;
                }else{
                    context.set_value(cname, Value::Empty)?;
                }
            }
            "TINYINT" => {
                let v = row.try_get::<i8, _>(col)?;
                context.set_value(cname, Value::Int(v as i64))?;
            }
            "SMALLINT" => {
                let v = row.try_get::<i16, _>(col)?;
                context.set_value(cname, Value::Int(v as i64))?;
            }
            "MEDIUMINT" | "INT" => {
                let v = row.try_get::<i32, _>(col)?;
                context.set_value(cname, Value::Int(v as i64))?;
            }
            "BIGINT" => {
                let v = row.try_get::<i64, _>(col)?;
                context.set_value(cname, Value::Int(v as i64))?;
            }
            "TINYINT UNSIGNED" => {
                let v = row.try_get::<u8, _>(col)?;
                context.set_value(cname, Value::Int(v as i64))?;
            }
            "SMALLINT UNSIGNED" => {
                let v = row.try_get::<u16, _>(col)?;
                context.set_value(cname, Value::Int(v as i64))?;
            }
            "MEDIUMINT UNSIGNED" | "INT UNSIGNED" => {
                let v = row.try_get::<u32, _>(col)?;
                context.set_value(cname, Value::Int(v as i64))?;
            }
            "BIGINT UNSIGNED" => {
                let v = row.try_get::<u64, _>(col)?;
                if v > i64::MAX as u64 {
                    anyhow::bail!("BIGINT UNSIGNED 超出范围");
                }
                context.set_value(cname, Value::Int(v as i64))?;
            }
            "FLOAT" => {
                let v = row.try_get::<f32, _>(col)?;
                context.set_value(cname, Value::from_float(v as f64))?;
            }
            "DOUBLE" => {
                let v = row.try_get::<f64, _>(col)?;
                context.set_value(cname, Value::from_float(v as f64))?;
            }
            "NULL" => {
                context.set_value(cname, Value::Empty)?;
            }
            "TIMESTAMP" | "DATETIME" => {
                let v = row.try_get::<chrono::NaiveDateTime, _>(col)?;
                let sv = v.format("%Y-%m-%d %H:%M:%S").to_string();
                context.set_value(cname, Value::from(sv))?;
            }
            "DATE" => {
                let v = row.try_get::<chrono::NaiveDate, _>(col)?;
                let sv = v.format("%Y-%m-%d").to_string();
                context.set_value(cname, Value::from(sv))?;
            }
            "TIME" => {
                let v = row.try_get::<chrono::NaiveTime, _>(col)?;
                let sv = v.format("%H:%M:%S").to_string();
                context.set_value(cname, Value::from(sv))?;
            }
            "YEAR" => {
                let v = row.try_get::<i32, _>(col)?;
                context.set_value(cname, Value::from_int(v as i64))?;
            },
            "CHAR"|"VARCHAR"  => {
                let v = row.try_get::<String, _>(col)?;
                context.set_value(cname, Value::from(v))?;
            },
            "DECIMAL" => {
                let v = row.try_get::<sqlx::types::Decimal, _>(col)?;
                let fv=v.to_f64().ok_or(anyhow::anyhow!("Decimal:{v} to f64 error"))?;
                context.set_value(cname, Value::from_float(fv))?;
            },
            _ => {
                return Err(anyhow::anyhow!("不支持的字段类型:{tp}"));
            },
        }
    }
    Ok(context)
}
